﻿using SketchUpAPI.NET.Enums;
using SketchUpAPI.NET.Objects;
using SketchUpAPI.NET.Objects.Tables;
using SketchUpAPI.NET.SketchUp.Exception;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;

namespace SketchUpAPI.NET.SketchUp
{
    /// <summary>
    /// SkerchUp模型
    /// </summary>
    /// <remarks>对应 C API 中的 SUModelRef 的相关功能及 SUInitialize 和 SUTerminate 两个函数</remarks>
    public class SketchUpModel : ObjectBase
    {
        private enum LoadStatus
        {
            /// <summary>
            /// 成功读取文件。
            /// </summary>
            Success,

            /// <summary>
            /// 成功读取文件，但是该文件是一个包版本SketchUp，模型数据可能存在缺失。
            /// </summary>
            Success_MoreRecent,
        }

        #region API函数
        /// <summary>
        /// 创建一个SketchUp模型
        /// </summary>
        /// <param name="modelRef"></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUModelCreate(out IntPtr modelRef);

        /// <summary>
        /// 从文件中读取模型
        /// </summary>
        /// <param name="modelRef">out，模型</param>
        /// <param name="path">模型路径</param>
        /// <param name="status">out，读取状态</param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUModelCreateFromFileWithStatus(out IntPtr modelRef, [MarshalAs(UnmanagedType.LPUTF8Str)] string path, out int status);

        /// <summary>
        /// 释放SketchUP模型
        /// </summary>
        /// <param name="modelRef">SketchUp模型</param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUModelRelease(ref IntPtr modelRef);

        /// <summary>
        /// 获取SketchUp模型中所有物体集合
        /// </summary>
        /// <param name="modelRef"></param>
        /// <param name="entitiesRef"><see cref="EntityTable"/></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUModelGetEntities(IntPtr modelRef, out IntPtr entitiesRef);

        /// <summary>
        /// 获取活动物件集
        /// </summary>
        /// <param name="model"></param>
        /// <param name="entities"><see cref="EntityTable"/></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUModelGetActiveEntities(IntPtr model, out IntPtr entities);

        /// <summary>
        /// 以特定版本保存模型到文件
        /// </summary>
        /// <param name="modelRef">模型</param>
        /// <param name="path">文件路径</param>
        /// <param name="version">模型版本，SUModelVersion</param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUModelSaveToFileWithVersion(IntPtr modelRef, [MarshalAs(UnmanagedType.LPUTF8Str)] string path, int version);

        /// <summary>
        /// 获取相机
        /// </summary>
        /// <param name="model"></param>
        /// <param name="camera"><see cref="Objects.Camera"/></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUModelGetCamera(IntPtr model, out IntPtr camera);

        /// <summary>
        /// 设置相机
        /// </summary>
        /// <param name="model"></param>
        /// <param name="camera"><see cref="Objects.Camera"/></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUModelSetCamera(IntPtr model, IntPtr camera);

        /// <summary>
        /// 获取当前版本
        /// </summary>
        /// <param name="model"></param>
        /// <param name="major"></param>
        /// <param name="minor"></param>
        /// <param name="build"></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUModelGetVersion(IntPtr model, out int major, out int minor, out int build);

        /// <summary>
        /// 获取属性字典的个数
        /// </summary>
        /// <param name="model"></param>
        /// <param name="count"></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUModelGetNumAttributeDictionaries(IntPtr model, out int count);

        /// <summary>
        /// 获取全部属性字典
        /// </summary>
        /// <param name="model"></param>
        /// <param name="len"></param>
        /// <param name="dictionaries"></param>
        /// <param name="realcount"></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUModelGetAttributeDictionaries(IntPtr model, int len, IntPtr[] dictionaries, out int realcount);

        /// <summary>
        /// 获取属性字典
        /// </summary>
        /// <param name="model"></param>
        /// <param name="name"></param>
        /// <param name="dictionary"></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUModelGetAttributeDictionary(IntPtr model, [MarshalAs(UnmanagedType.LPUTF8Str)] string name, out IntPtr dictionary);

        /// <summary>
        /// 模型是否有地理参考
        /// </summary>
        /// <param name="model"></param>
        /// <param name="is_geo_ref"></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUModelIsGeoReferenced(IntPtr model, out bool is_geo_ref);

        /// <summary>
        /// 获取地理位置
        /// </summary>
        /// <param name="model"></param>
        /// <param name="location"><see cref="Objects.Location"/></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUModelGetLocation(IntPtr model, out IntPtr location);

        /// <summary>
        /// 设置地理信息
        /// </summary>
        /// <param name="model"></param>
        /// <param name="latitude">维度 [-90, 90]</param>
        /// <param name="longitude">经度 [-180, 180]</param>
        /// <param name="altitude">海拔</param>
        /// <param name="is_z_value_centered">Z 值是否居中</param>
        /// <param name="is_on_ocean_floor">是否位于海平面</param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUModelSetGeoReference(IntPtr model, double latitude, double longitude, double altitude, bool is_z_value_centered, bool is_on_ocean_floor);

        /// <summary>
        /// 获取渲染选项
        /// </summary>
        /// <param name="model"></param>
        /// <param name="rendering_options"><see cref="Objects.RenderingOptions"/></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUModelGetRenderingOptions(IntPtr model, out IntPtr rendering_options);

        /// <summary>
        /// 获取阴影设置
        /// </summary>
        /// <param name="model"></param>
        /// <param name="shadow_info"><see cref="Objects.ShadowInfo"/></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUModelGetShadowInfo(IntPtr model, out IntPtr shadow_info);

        /// <summary>
        /// 获取设置管理器
        /// </summary>
        /// <param name="model"></param>
        /// <param name="options_manager"></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUModelGetOptionsManager(IntPtr model, out IntPtr options_manager);

        /// <summary>
        /// 北极相对于Y轴的角度
        /// </summary>
        /// <param name="model"></param>
        /// <param name="north_correction"></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUModelGetNorthCorrection(IntPtr model, out double north_correction);

        /// <summary>
        /// 合并模型中所有相邻的共面面
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUModelMergeCoplanarFaces(IntPtr model);

        /// <summary>
        /// 获取模型名字
        /// </summary>
        /// <param name="model"></param>
        /// <param name="name"><see cref="StringRef"/></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUModelGetName(IntPtr model, ref IntPtr name);

        /// <summary>
        /// 设置模型名字
        /// </summary>
        /// <param name="model"></param>
        /// <param name="name"></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUModelSetName(IntPtr model, [MarshalAs(UnmanagedType.LPUTF8Str)] string name);

        /// <summary>
        /// 获取模型路径
        /// </summary>
        /// <param name="model"></param>
        /// <param name="path"><see cref="StringRef"/></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUModelGetPath(IntPtr model, ref IntPtr path);

        /// <summary>
        /// 获取标题
        /// </summary>
        /// <param name="model"></param>
        /// <param name="title"><see cref="StringRef"/></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUModelGetTitle(IntPtr model, ref IntPtr title);

        /// <summary>
        /// 获取说明
        /// </summary>
        /// <param name="model"></param>
        /// <param name="description"><see cref="StringRef"/></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUModelGetDescription(IntPtr model, ref IntPtr description);

        /// <summary>
        /// 设置说明
        /// </summary>
        /// <param name="model"></param>
        /// <param name="description"></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUModelSetDescription(IntPtr model, [MarshalAs(UnmanagedType.LPUTF8Str)] string description);

        /// <summary>
        /// 获取模型的单位
        /// </summary>
        /// <param name="modelRef"></param>
        /// <param name="unit"></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUModelGetUnits(IntPtr modelRef, out int unit);

        /// <summary>
        /// 坐标系
        /// </summary>
        /// <param name="model"></param>
        /// <param name="axes"><see cref="Objects.Axes"/></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUModelGetAxes(IntPtr model, out IntPtr axes);

        /// <summary>
        /// 获取样式表
        /// </summary>
        /// <param name="model"></param>
        /// <param name="style"></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUModelGetStyles(IntPtr model, out IntPtr style);

        /// <summary>
        /// 通过物体的pid获取指向该物体引用路径
        /// </summary>
        /// <param name="model"></param>
        /// <param name="pid_ref"><see cref="StringRef"/></param>
        /// <param name="instance_path_ref"></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUModelGetInstancePathByPid(IntPtr model, IntPtr pid_ref, out IntPtr instance_path_ref);

        /// <summary>
        /// 获取字体个数
        /// </summary>
        /// <param name="model"></param>
        /// <param name="count"></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUModelGetNumFonts(IntPtr model, out int count);

        /// <summary>
        /// 获取全部字体
        /// </summary>
        /// <param name="model"></param>
        /// <param name="len"></param>
        /// <param name="fonts"><see cref="Objects.Font"/></param>
        /// <param name="realcount"></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUModelGetFonts(IntPtr model, int len, IntPtr[] fonts, out int realcount);

        /// <summary>
        /// 获取标注样式
        /// </summary>
        /// <param name="model"></param>
        /// <param name="style"><see cref="Objects.DimensionStyle"/></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUModelGetDimensionStyle(IntPtr model, out IntPtr style);

        /// <summary>
        /// 读取尺寸格式化器
        /// </summary>
        /// <param name="modelRef"></param>
        /// <param name="formatter"><see cref="Objects.LengthFormatter"/></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUModelGetLengthFormatter(IntPtr modelRef, ref IntPtr formatter);

        /// <summary>
        /// 在已有名字的基础上迭代一个唯一材质名称
        /// </summary>
        /// <param name="model"></param>
        /// <param name="in_name"></param>
        /// <param name="out_name"></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUModelGenerateUniqueMaterialName(IntPtr model, [MarshalAs(UnmanagedType.LPUTF8Str)] string in_name, ref IntPtr out_name);

        /// <summary>
        /// 修复错误
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUModelFixErrors(IntPtr model);

        /// <summary>
        /// 更新模型中的面，使其方向一致
        /// </summary>
        /// <param name="model"></param>
        /// <param name="recurse_components"></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUModelOrientFacesConsistently(IntPtr model, bool recurse_components);

        /// <summary>
        /// 线型表
        /// </summary>
        /// <param name="model"></param>
        /// <param name="line_styles"><see cref="LineStyleTable"/></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUModelGetLineStyles(IntPtr model, out IntPtr line_styles);

        /// <summary>
        /// 加载组件
        /// </summary>
        /// <param name="model"></param>
        /// <param name="filename"></param>
        /// <param name="definition"><see cref="Objects.ComponentDefinition"/></param>
        /// <param name="status"></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUModelLoadDefinitionWithStatus(IntPtr model, [MarshalAs(UnmanagedType.LPUTF8Str)] string filename, out IntPtr definition, out LoadStatus status);

        /// <summary>
        /// 获取GUID
        /// </summary>
        /// <param name="model"></param>
        /// <param name="guid"></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUModelGetGuid(IntPtr model, ref IntPtr guid);

        /// <summary>
        /// 物体是否可见
        /// </summary>
        /// <param name="model"></param>
        /// <param name="path"><see cref="Objects.InstancePath"/></param>
        /// <param name="visible"></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUModelIsDrawingElementVisible(IntPtr model, IntPtr path, out bool visible);

        /// <summary>
        /// 获取组件行为
        /// </summary>
        /// <param name="model"></param>
        /// <param name="behavior"></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUModelGetBehavior(IntPtr model, ref ComponentBehavior behavior);

        /// <summary>
        /// 设置组件行为
        /// </summary>
        /// <param name="model"></param>
        /// <param name="behavior"></param>
        /// <returns></returns>
        [DllImport(LIB)]
        static extern SUResult SUModelSetBehavior(IntPtr model, ComponentBehavior behavior);
        #endregion

        /// <summary>
        /// 组件行为
        /// </summary>
        /// <remarks></remarks>
        public ComponentBehavior ComponentBehavior
        {
            get
            {
                ComponentBehavior cb = new ComponentBehavior();
                SketchUpExceptionHelper.ThrowSUResult(SUModelGetBehavior(Id, ref cb));
                return cb;
            }
            set
            {
                SketchUpExceptionHelper.ThrowSUResult(SUModelSetBehavior(Id, value));
            }
        }

        /// <summary>
        /// 模型Guid
        /// </summary>
        public string Guid
        {
            get
            {
                StringRef s = StringRef.Empty;
                SketchUpExceptionHelper.ThrowSUResult(SUModelGetGuid(Id, ref s.Id));
                return s.Dispose();
            }
        }

        /// <summary>
        /// 模型名称
        /// </summary>
        public string Name
        {
            get
            {
                StringRef s = StringRef.Empty;
                SketchUpExceptionHelper.ThrowSUResult(SUModelGetName(Id, ref s.Id));
                return s.Dispose();
            }
            set
            {
                SketchUpExceptionHelper.ThrowSUResult(SUModelSetName(Id, value));
            }
        }

        /// <summary>
        /// 模型路径
        /// </summary>
        public string Path
        {
            get
            {
                StringRef s = StringRef.Empty;
                SketchUpExceptionHelper.ThrowSUResult(SUModelGetPath(Id, ref s.Id));
                return s.Dispose();
            }
        }

        /// <summary>
        /// 模型标题
        /// </summary>
        public string Title
        {
            get
            {
                StringRef s = StringRef.Empty;
                SketchUpExceptionHelper.ThrowSUResult(SUModelGetTitle(Id, ref s.Id));
                return s.Dispose();
            }
        }

        /// <summary>
        /// 模型说明
        /// </summary>
        public string Description
        {
            get
            {
                StringRef s = StringRef.Empty;
                SketchUpExceptionHelper.ThrowSUResult(SUModelGetDescription(Id, ref s.Id));
                return s.Dispose();
            }
            set
            {
                SketchUpExceptionHelper.ThrowSUResult(SUModelSetDescription(Id, value));
            }
        }

        /// <summary>
        /// 版本
        /// </summary>
        public string Version
        {
            get
            {
                SketchUpExceptionHelper.ThrowSUResult(SUModelGetVersion(Id, out int m, out int mi, out int bu));
                return $"{m}.{mi}.{bu}";
            }
        }

        /// <summary>
        /// 模型使用的单位
        /// </summary>
        public UnitLengthType ModelUnit
        {
            get
            {
                var res = (SUResult)SUModelGetUnits(Id, out int u);
                if (res != SUResult.SU_ERROR_NONE) SketchUpExceptionHelper.ThrowSUResult(res);
                return (UnitLengthType)u;
            }
        }

        /// <summary>
        /// 获取物件表
        /// </summary>
        public EntityTable Entities
        {
            get
            {
                SketchUpExceptionHelper.ThrowSUResult(SUModelGetEntities(Id, out IntPtr id));
                return new EntityTable(id);
            }
        }

        /// <summary>
        /// 激活的物件集
        /// </summary>
        /// <remarks>活动上下文的物件集，比如进入群组或组件时的物件集</remarks>
        public EntityTable ActiveEntities
        {
            get
            {
                SketchUpExceptionHelper.ThrowSUResult(SUModelGetActiveEntities(Id, out IntPtr id));
                return new EntityTable(id);
            }
        }

        /// <summary>
        /// 设置管理器
        /// </summary>
        public OptionsProviderTable OptionsManager
        {
            get
            {
                SketchUpExceptionHelper.ThrowSUResult(SUModelGetOptionsManager(Id, out IntPtr id));
                return new OptionsProviderTable(id);
            }
        }

        /// <summary>
        /// 获取样式表
        /// </summary>
        public StyleTable Styles
        {
            get
            {
                SketchUpExceptionHelper.ThrowSUResult(SUModelGetStyles(Id, out IntPtr id));
                return new StyleTable(id);
            }
        }

        /// <summary>
        /// 获取组件定义表
        /// </summary>
        public ComponentDefinitionTable ComponentDefinitions
        {
            get
            {
                return new ComponentDefinitionTable(Id);
            }
        }

        /// <summary>
        /// 材质表
        /// </summary>
        public MaterialTable Materials
        {
            get
            {
                return new MaterialTable(Id);
            }
        }

        /// <summary>
        /// 群组表
        /// </summary>
        public GroupTable Groups
        {
            get
            {
                return new GroupTable(Id);
            }
        }

        /// <summary>
        /// 线型表
        /// </summary>
        public LineStyleTable LineStyles
        {
            get
            {
                SketchUpExceptionHelper.ThrowSUResult(SUModelGetLineStyles(Id, out IntPtr id));
                return new LineStyleTable(id);
            }
        }

        /// <summary>
        /// 所有属性字典
        /// </summary>
        public List<AttributeDictionary> AttributeDictionaries
        {
            get
            {
                SketchUpExceptionHelper.ThrowSUResult(SUModelGetNumAttributeDictionaries(Id, out int count));
                IntPtr[] pts = new IntPtr[count];
                SketchUpExceptionHelper.ThrowSUResult(SUModelGetAttributeDictionaries(Id, count, pts, out _));
                return pts.Select(i => new AttributeDictionary(i)).ToList();
            }
        }

        /// <summary>
        /// 相机
        /// </summary>
        public Camera Camera
        {
            get
            {
                SketchUpExceptionHelper.ThrowSUResult(SUModelGetCamera(Id, out IntPtr id));
                return new Camera(id);
            }
            set
            {
                SketchUpExceptionHelper.ThrowSUResult(SUModelSetCamera(Id, value.Id));
            }
        }

        /// <summary>
        /// 是否设置了地理位置
        /// </summary>
        public bool IsGeoReferenced
        {
            get
            {
                SketchUpExceptionHelper.ThrowSUResult(SUModelIsGeoReferenced(Id, out bool g));
                return g;
            }
        }

        /// <summary>
        /// 地理位置
        /// </summary>
        public Location Location
        {
            get
            {
                SketchUpExceptionHelper.ThrowSUResult(SUModelGetLocation(Id, out IntPtr l));
                return new Location(l);
            }
        }

        /// <summary>
        /// 显示设置
        /// </summary>
        public RenderingOptions RenderingOptions
        {
            get
            {
                SketchUpExceptionHelper.ThrowSUResult(SUModelGetRenderingOptions(Id, out IntPtr l));
                return new RenderingOptions(l);
            }
        }

        /// <summary>
        /// 阴影设置
        /// </summary>
        public ShadowInfo ShadowInfo
        {
            get
            {
                SketchUpExceptionHelper.ThrowSUResult(SUModelGetShadowInfo(Id, out IntPtr l));
                return new ShadowInfo(l);
            }
        }

        /// <summary>
        /// 北极相对于Y轴的角度
        /// </summary>
        public double NorthCorrection
        {
            get
            {
                SketchUpExceptionHelper.ThrowSUResult(SUModelGetNorthCorrection(Id, out double a));
                return a;
            }
        }

        /// <summary>
        /// 坐标系
        /// </summary>
        public Axes Axes
        {
            get
            {
                SketchUpExceptionHelper.ThrowSUResult(SUModelGetAxes(Id, out IntPtr l));
                return new Axes(l);
            }
        }

        /// <summary>
        /// 获取所有字体
        /// </summary>
        public List<Font> Fonts
        {
            get
            {
                SketchUpExceptionHelper.ThrowSUResult(SUModelGetNumFonts(Id, out int count));
                IntPtr[] pts = new IntPtr[count];
                SketchUpExceptionHelper.ThrowSUResult(SUModelGetFonts(Id, count, pts, out _));
                return pts.Select(i => new Font(i)).ToList();
            }
        }

        /// <summary>
        /// 标注样式
        /// </summary>
        public DimensionStyle DimensionStyle
        {
            get
            {
                SketchUpExceptionHelper.ThrowSUResult(SUModelGetDimensionStyle(Id, out IntPtr l));
                return new DimensionStyle(l);
            }
        }

        /// <summary>
        /// 长度格式化器
        /// </summary>
        public LengthFormatter LengthFormatter
        {
            get
            {
                LengthFormatter lm = new LengthFormatter();
                SketchUpExceptionHelper.ThrowSUResult(SUModelGetLengthFormatter(Id, ref lm.Id));
                return lm;
            }
        }

        internal SketchUpModel(IntPtr id) => Id = id;

        /// <summary>
        /// 新建一个SketchUP
        /// </summary>
        /// <returns></returns>
        public static SketchUpModel Create()
        {
            SketchUpExceptionHelper.ThrowSUResult(SUModelCreate(out IntPtr id));
            SketchUpModel skp = new SketchUpModel(id);

            SetMilimeterUnits(skp);

            string style = "TemplateStyle.style";
            if (System.IO.File.Exists(style)) skp.Styles.AddStyle(style, true);

            return skp;
        }

        /// <summary>
        /// 从文件中读取模型
        /// </summary>
        /// <remarks>从 SketchUp2021.0 开始 SketchUpAPI 可以读取更高版本文件，但是高版本才有的数据将会被放弃</remarks>
        /// <param name="path"></param>
        /// <param name="higherVersion">是否是更高版本，如果读取模型失败，则该状态无意义</param>
        /// <returns></returns>
        public static SketchUpModel Read(string path, out bool higherVersion)
        {
            SketchUpExceptionHelper.ThrowSUResult(SUModelCreateFromFileWithStatus(out IntPtr id, path, out int status));
            SketchUpModel skp = new SketchUpModel(id);
            higherVersion = (LoadStatus)status == LoadStatus.Success_MoreRecent;
            return skp;
        }

        /// <summary>
        /// 保存模型
        /// </summary>
        /// <param name="path">路径</param>
        /// <param name="version">版本，默认最新版</param>
        /// <returns></returns>
        public void Save(string path, SUModelVersion version = SUModelVersion.Current)
        {
            SketchUpExceptionHelper.ThrowSUResult(SUModelSaveToFileWithVersion(Id, path, (int)version));
        }

        /// <summary>
        /// 通过一个物体的PID获取指向这个物体的引用路径
        /// </summary>
        /// <param name="entityPID"></param>
        /// <returns></returns>
        public InstancePath CreateInstancePathByPid(int entityPID)
        {
            StringRef s = new StringRef(entityPID.ToString());
            SketchUpExceptionHelper.ThrowSUResult(SUModelGetInstancePathByPid(Id, s.Id, out IntPtr p));
            return new InstancePath(p);
        }

        /// <summary>
        /// 获取属性字典
        /// </summary>
        /// <param name="name">字典的名字</param>
        /// <returns></returns>
        public AttributeDictionary GetAttributeDictionary(string name)
        {
            SketchUpExceptionHelper.ThrowSUResult(SUModelGetAttributeDictionary(Id, name, out IntPtr d));
            return new AttributeDictionary(d);
        }

        /// <summary>
        /// 设置地理信息
        /// </summary>
        /// <param name="latitude">维度 [-90, 90]</param>
        /// <param name="longitude">经度 [-180, 180]</param>
        /// <param name="altitude">海拔</param>
        /// <param name="is_z_value_centered">Z 值是否居中</param>
        /// <param name="is_on_ocean_floor">是否位于海平面</param>
        /// <returns></returns>
        public void SetGeoReference(double latitude, double longitude, double altitude, bool is_z_value_centered, bool is_on_ocean_floor)
        {
            SketchUpExceptionHelper.ThrowSUResult(SUModelSetGeoReference(Id, latitude, longitude, altitude, is_z_value_centered, is_on_ocean_floor));
        }

        /// <summary>
        /// 合并模型中所有相邻的共面面
        /// </summary>
        public void MergeCoplanarFaces()
        {
            SketchUpExceptionHelper.ThrowSUResult(SUModelMergeCoplanarFaces(Id));
        }

        /// <summary>
        /// 修复错误
        /// </summary>
        public void FixErrors()
        {
            SketchUpExceptionHelper.ThrowSUResult(SUModelFixErrors(Id));
        }

        /// <summary>
        /// 使模型中的面方向一致
        /// </summary>
        /// <param name="orientComponent">是否处理组件中的面</param>
        public void OrientFacesConsistently(bool orientComponent)
        {
            SketchUpExceptionHelper.ThrowSUResult(SUModelOrientFacesConsistently(Id, orientComponent));
        }

        /// <summary>
        /// 以已有的名字为基础，迭代一个唯一的材质名称
        /// </summary>
        /// <param name="baseName"></param>
        /// <returns></returns>
        public string GenerateUniqueMaterialName(string baseName)
        {
            StringRef s = StringRef.Empty;
            SketchUpExceptionHelper.ThrowSUResult(SUModelGenerateUniqueMaterialName(Id, baseName, ref s.Id));
            return s.Dispose();
        }

        /// <summary>
        /// 从文件加载组件
        /// </summary>
        /// <param name="file"></param>
        /// <param name="higherVersion"></param>
        /// <returns></returns>
        public ComponentDefinition LoadComponentDefinition(string file, out bool higherVersion)
        {
            SketchUpExceptionHelper.ThrowSUResult(SUModelLoadDefinitionWithStatus(Id, file, out IntPtr d, out LoadStatus s));
            if (s == LoadStatus.Success) higherVersion = false;
            else higherVersion = true;

            return new ComponentDefinition(d);
        }

        /// <summary>
        /// 物体是否可见
        /// </summary>
        /// <param name="element"></param>
        /// <returns></returns>
        public bool IsElementVisible(DrawingElement element)
        {
            SketchUpExceptionHelper.ThrowSUResult(SUModelIsDrawingElementVisible(Id, element.Id, out bool v));
            return v;
        }

        /// <summary>
        /// 释放全部资源
        /// </summary>
        public override void Dispose()
        {
            SketchUpExceptionHelper.ThrowSUResult(SUModelRelease(ref Id));
        }

        /// <summary>
        /// 设置毫米系列单位（精度保留整数）
        /// </summary>
        /// <remarks>毫米、平方毫米、立方毫米</remarks>
        /// <param name="skp"></param>
        private static void SetMilimeterUnits(SketchUpModel skp)
        {
            var om = skp.OptionsManager;
            var op = om.UnitsOptions;
            op.LengthFormat = LengthFormatType.Decimal;

            op.LengthUnit = UnitLengthType.Millimeters;
            op.LengthPrecision = Precision.Zero;

            op.AreaUnit = UnitAreaType.Millimeter;
            op.AreaPrecision = Precision.Zero;

            op.VolumeUnit = UnitVolumeType.Millimeter;
            op.VolumePrecision = Precision.Zero;
        }
    }
}